package com.bei_en.common.util.OSS.demo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.ListPartsRequest;
import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.model.PartListing;
import com.aliyun.oss.model.PartSummary;
import com.aliyun.oss.model.UploadPartRequest;
import com.aliyun.oss.model.UploadPartResult;

/**
 * This sample demonstrates how to upload multiparts to Aliyun OSS using the OSS
 * SDK for Java.
 */
public class MultipartUploadSample {

	private static String endpoint = "oss-cn-shenzhen.aliyuncs.com";
	private static String accessKeyId = "uaRj1UVNYFY6rV1V";
	private static String accessKeySecret = "uEAQR3pgn1uZLITNwEGozLHIJAkgbm";

	private static OSSClient client = null;

	private static String bucketName = "che-user-pic";
	private static String key = "*** Provide key ***";
	private static String localFilePath = "d:/1.jpg";

	private static ExecutorService executorService = Executors
			.newFixedThreadPool(5);
	private static List<PartETag> partETags = Collections
			.synchronizedList(new ArrayList<PartETag>());

	public static void main(String[] args) throws IOException {

		/*
		 * Constructs a client instance with your account for accessing OSS
		 */
		client = new OSSClient(endpoint, accessKeyId, accessKeySecret);

		try {
			/*
			 * Claim a upload id firstly
			 */
			String uploadId = claimUploadId();
			System.out.println("Claiming a new upload id " + uploadId + "\n");

			/*
			 * Calculate how many parts to be divided
			 */
			final long partSize = 5 * 1024 * 1024L; // 5MB
			final File sampleFile = createSampleFile();
			long fileLength = sampleFile.length();
			int partCount = (int) (fileLength / partSize);
			if (fileLength % partSize != 0) {
				partCount++;
			}
			if (partCount > 10000) {
				throw new RuntimeException(
						"Total parts count should not exceed 10000");
			} else {
				System.out.println("Total parts count " + partCount + "\n");
			}

			/*
			 * Upload multiparts to your bucket
			 */
			System.out
					.println("Begin to upload multiparts to OSS from a file\n");
			for (int i = 0; i < partCount; i++) {
				long startPos = i * partSize;
				long curPartSize = (i + 1 == partCount) ? (fileLength - startPos)
						: partSize;
				executorService.execute(new PartUploader(sampleFile, startPos,
						curPartSize, i + 1, uploadId));
			}

			/*
			 * Waiting for all parts finished
			 */
			executorService.shutdown();
			while (!executorService.isTerminated()) {
				try {
					executorService.awaitTermination(5, TimeUnit.SECONDS);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			/*
			 * Verify whether all parts are finished
			 */
			if (partETags.size() != partCount) {
				throw new IllegalStateException(
						"Upload multiparts fail due to some parts are not finished yet");
			} else {
				System.out
						.println("Succeed to complete multiparts into an object named "
								+ key + "\n");
			}

			/*
			 * View all parts uploaded recently
			 */
			listAllParts(uploadId);

			/*
			 * Complete to upload multiparts
			 */
			completeMultipartUpload(uploadId);

			/*
			 * Fetch the object that newly created at the step below.
			 */
			System.out.println("Fetching an object");
			client.getObject(new GetObjectRequest(bucketName, key), new File(
					localFilePath));

		} catch (OSSException oe) {
			System.out
					.println("Caught an OSSException, which means your request made it to OSS, "
							+ "but was rejected with an error response for some reason.");
			System.out.println("Error Message: " + oe.getErrorCode());
			System.out.println("Error Code:       " + oe.getErrorCode());
			System.out.println("Request ID:      " + oe.getRequestId());
			System.out.println("Host ID:           " + oe.getHostId());
		} catch (ClientException ce) {
			System.out
					.println("Caught an ClientException, which means the client encountered "
							+ "a serious internal problem while trying to communicate with OSS, "
							+ "such as not being able to access the network.");
			System.out.println("Error Message: " + ce.getMessage());
		} finally {
			/*
			 * Do not forget to shut down the client finally to release all
			 * allocated resources.
			 */
			if (client != null) {
				client.shutdown();
			}
		}
	}

	private static class PartUploader implements Runnable {

		private File localFile;
		private long startPos;

		private long partSize;
		private int partNumber;
		private String uploadId;

		public PartUploader(File localFile, long startPos, long partSize,
				int partNumber, String uploadId) {

			this.localFile = localFile;
			this.startPos = startPos;
			this.partSize = partSize;
			this.partNumber = partNumber;
			this.uploadId = uploadId;
		}

		@Override
		public void run() {

			InputStream instream = null;
			try {
				instream = new FileInputStream(this.localFile);
				instream.skip(this.startPos);

				UploadPartRequest uploadPartRequest = new UploadPartRequest();
				uploadPartRequest.setBucketName(bucketName);
				uploadPartRequest.setKey(key);
				uploadPartRequest.setUploadId(this.uploadId);
				uploadPartRequest.setInputStream(instream);
				uploadPartRequest.setPartSize(this.partSize);
				uploadPartRequest.setPartNumber(this.partNumber);

				UploadPartResult uploadPartResult = client
						.uploadPart(uploadPartRequest);
				System.out.println("Part#" + this.partNumber + " done\n");
				synchronized (partETags) {
					partETags.add(uploadPartResult.getPartETag());
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (instream != null) {
					try {
						instream.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	private static File createSampleFile() throws IOException {

		File file = File.createTempFile("oss-java-sdk-", ".txt");
		file.deleteOnExit();

		Writer writer = new OutputStreamWriter(new FileOutputStream(file));
		for (int i = 0; i < 1000000; i++) {
			writer.write("abcdefghijklmnopqrstuvwxyz\n");
			writer.write("0123456789011234567890\n");
		}
		writer.close();

		return file;
	}

	private static String claimUploadId() {

		InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(
				bucketName, key);
		InitiateMultipartUploadResult result = client
				.initiateMultipartUpload(request);
		return result.getUploadId();
	}

	private static void completeMultipartUpload(String uploadId) {

		// Make part numbers in ascending order
		Collections.sort(partETags, new Comparator<PartETag>() {

			@Override
			public int compare(PartETag p1, PartETag p2) {

				return p1.getPartNumber() - p2.getPartNumber();
			}
		});

		System.out.println("Completing to upload multiparts\n");
		CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
				bucketName, key, uploadId, partETags);
		client.completeMultipartUpload(completeMultipartUploadRequest);
	}

	private static void listAllParts(String uploadId) {

		System.out.println("Listing all parts......");
		ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName,
				key, uploadId);
		PartListing partListing = client.listParts(listPartsRequest);

		int partCount = partListing.getParts().size();
		for (int i = 0; i < partCount; i++) {
			PartSummary partSummary = partListing.getParts().get(i);
			System.out.println("\tPart#" + partSummary.getPartNumber()
					+ ", ETag=" + partSummary.getETag());
		}
		System.out.println();
	}
}
